#ifndef __THREADCACHE_HPP__
#define __THREADCACHE_HPP__
// 第一层 线程缓冲区

#include "Common.hpp"
#include "CentralCache.hpp"

namespace QiHai
{
	class ThreadCache
	{
	private:
		FreeList _freeList[NFREELIST];  // 内存划分 <= 256kb 内存对齐有208个桶哦
	public:
		// 申请
		void* Allocate(size_t bytes);
		void* FetchFromCentralcache(size_t index, size_t byte);

		// 释放
		void Deallocate(void* ptr, size_t byte);
		void ListTooLong(FreeList& list, size_t byte);
	};

	// TLS 让每个线程看到不同的ThreadCache对象
	static __declspec(thread) ThreadCache* TLSThradObj = nullptr;

	///////////////ThreadCache///////////////

	// ThreadCache申请内存入口
	void* ThreadCache::Allocate(size_t bytes)
	{
		// 传入的内存大小还没有对齐，首先对齐
		size_t byte = SizeClass::RoundUp(bytes);
		// 算出哈希桶桶号
		size_t num = SizeClass::Index(bytes);

		// 首先查看对应桶号是否存在内存，存在直接返回，否则需要申请内存
		if (!_freeList[num].Empty()) return _freeList[num].HeadPop();
		// 否则就需要向上层申请内存了
		return FetchFromCentralcache(num, byte);  // 传入的是对齐后的大小以及哈希桶编号
	}

	// 向centralcache申请内存 byte对齐字节数 index-哈希桶编号
	void* ThreadCache::FetchFromCentralcache(size_t index, size_t byte)
	{
		// 既然是申请内存，那么我们最好申请一批内存
		// 那么现在想好，每个类型对应申请多大批的内存合适呢？
		// 注意，我们需要慢启动来提升内存的利用率哦
		size_t sizes = _freeList[index].getMaxSize() < SizeClass::NumMoveSize(byte) ? \
					   _freeList[index].getMaxSize()++ : SizeClass::NumMoveSize(byte);

		void* start = nullptr;
		sizes = CentralCache::CentralCacheObj()->FetchRangeObj(byte, start, sizes);
		// 此时sizes是真实返回的大小
		// 注意，此时将此自由链表进行一个切分，将头甩出去其余链入哈希桶中
		if (sizes > 1)
		{
			_freeList[index].PushRange(NextObj(start), sizes - 1);
		}
		return start;
	}

	// ThreadCache释放内存入口，会根据对应类型算出桶位置，插入自由链表中去-注意，如果此时插入的长度大于了想要申请的长度，那么应该还回去了
	void ThreadCache::Deallocate(void* ptr, size_t byte)
	{
		size_t index = SizeClass::Index(byte);
		_freeList[index].Headinsert(ptr);

		if (_freeList[index].getSize() >= _freeList[index].getMaxSize())
		{
			// 需要向上还了
			ListTooLong(_freeList[index], byte);
		}
	}

	void ThreadCache::ListTooLong(FreeList& list, size_t byte)
	{
		// 将需要还回去的一串自由链表还回CentralCache的说
		void* start = nullptr;
		list.PopRange(start, list.getMaxSize());
		// 然后就是往上层进行传递

		CentralCache::CentralCacheObj()->ReleaseListToSpans(start, byte);
	}
}

#endif // !__THREADCACHE_HPP__
